#ifndef __w2b_h_
#define __w2b_h_

#ifndef _DATA_TYPE
#define _DATA_TYPE
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef long LONG;
#endif

#define BMP_TYPE 0x4D42
//#define RATIO 1 //变换比例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "BMP.h"
#include "WAVE.h"
#include "err.h"

int chkID(const char id[4]); //检查CHUNKID
WAV_CACHE *cacheWaveFile(char *path);		  //建立指定wav文件缓存
void delCacheWaveFile(WAV_CACHE *cache);	  //清除wav文件缓存
DWORD listWavData(WAV_CACHE *cache);		  //列出wav文件PCM数据(测试用)
BMP4bit_CACHE *cache4bitBmp(DWORD w, LONG h); //缓存4bitBMP

int chkID(const char id[4]) //检查CHUNKID
{
	int i = 0;
	int j = 0;
	int outID = -1;

	char okID[5][4] =
		{
			{'R', 'I', 'F', 'F'},
			{'W', 'A', 'V', 'E'},
			{'f', 'm', 't', ' '},
			{'f', 'a', 'c', 't'},
			{'d', 'a', 't', 'a'}};
	for (i = 0; i < 5; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (okID[i][j] == id[j])
			{
				outID = i;
			}
			else
			{
				outID = -1;
				break;
			}
		}
		if (outID != -1)
			return outID;
	}
	return -1;
}

WAV_CACHE *cacheWaveFile(char *path)
{
	FILE *fp = NULL;
	BYTE *cache = NULL;		 //缓存指针
	WAVE_RIFF *pHEAD = NULL; //RIFF指针
	WAVE_FMT *pFMT = NULL;   //格式指针
	WAVE_FACT *pFACT = NULL; //FACT指针
	WAVE_DATA *pDATA = NULL; //数据指针
	WAV_CACHE *pOUT = NULL;

	fp = fopen(path, "rb");
	if (NULL == fp)
	{
		err(FileErr);
		return NULL;
	}

	cache = (BYTE *)calloc(sizeof(WAVE_RIFF), sizeof(BYTE)); //分配wav文件头缓存空间(WAVE_RIFF)
	if (NULL == cache)
	{
		err(MemErr);
		fclose(fp);
		return NULL;
	}

	fread(cache, sizeof(BYTE), sizeof(WAVE_RIFF), fp); //缓存wav文件头

	pHEAD = (WAVE_RIFF *)cache;												//定位文件头
	if ((chkID(pHEAD->strRiffID) != 0) || (chkID(pHEAD->strRiffType) != 1)) //检查文件头合法性
	{
		err(TypeErr);
		free(cache);
		fclose(fp);
		pHEAD = NULL;
		cache = NULL;
		return NULL;
	}

	cache = (BYTE *)calloc((pHEAD->dwRiffSize) - 8, sizeof(BYTE)); //分配wav文件缓存空间(不包含WACE_RIFF)
	if (NULL == cache)
	{
		err(MemErr);
		free(pHEAD);
		fclose(fp);
		pHEAD = NULL;
		return NULL;
	}

	fread(cache, sizeof(BYTE), (pHEAD->dwRiffSize) - 8, fp); //缓存wav文件剩余部分
	pFMT = (WAVE_FMT *)cache;
	if (chkID(pFMT->strFmtID) != 2)
	{
		err(TypeErr);
		free(cache);
		free(pHEAD);
		fclose(fp);
		pFMT = NULL;
		pHEAD = NULL;
		cache = NULL;
		return NULL;
	}

	pFACT = (WAVE_FACT *)((BYTE *)pFMT + 8 + (pFMT->dwFmtSize)); //定位FACT
	if (chkID(pFACT->strFactID) != 3)							 //FACT检查
	{
		if (4 == chkID(pFACT->strFactID)) //Not have FACT
		{
			pDATA = (WAVE_DATA *)pFACT;
			pFACT = NULL;
		}
		else
		{
			err(TypeErr);
			free(cache);
			free(pHEAD);
			fclose(fp);
			pFACT = NULL;
			pFMT = NULL;
			pHEAD = NULL;
			cache = NULL;
			return NULL;
		}
	}
	else
	{
		pDATA = (WAVE_DATA *)((BYTE *)pFACT + 8 + (pFACT->dwFactSize)); //定位数据
		if (4 != chkID(pDATA->strDataID))
		{
			err(TypeErr);
			free(cache);
			free(pHEAD);
			fclose(fp);
			pDATA = NULL;
			pFACT = NULL;
			pFMT = NULL;
			pHEAD = NULL;
			cache = NULL;
			return NULL;
		}
	}

	pDATA->pWaveData = (BYTE *)pDATA + 8; //定位PCM数据位置

	pOUT = (WAV_CACHE *)calloc(sizeof(WAV_CACHE), sizeof(BYTE));
	if (NULL == pOUT)
	{
		err(MemErr);
		free(cache);
		free(pHEAD);
		fclose(fp);
		pFACT = NULL;
		pFMT = NULL;
		pHEAD = NULL;
		cache = NULL;
		return NULL;
	}
	memcpy(pOUT, (BYTE *)pFMT + 8, sizeof(WAVE_FORMAT));
	pOUT->DataSize = pDATA->dwDataSize;
	pOUT->DataCache = calloc(pOUT->DataSize, sizeof(BYTE));
	if (NULL == pOUT->DataCache)
	{
		err(MemErr);
		free(cache);
		free(pHEAD);
		free(pOUT);
		fclose(fp);
		pFACT = NULL;
		pFMT = NULL;
		pHEAD = NULL;
		cache = NULL;
		pOUT = NULL;
		return NULL;
	}
	memcpy(pOUT->DataCache, pDATA->pWaveData, pOUT->DataSize);
	free(cache);
	free(pHEAD);
	fclose(fp);
	pFACT = NULL;
	pFMT = NULL;
	pHEAD = NULL;
	cache = NULL;
	return pOUT;
}

void delCacheWaveFile(WAV_CACHE *cache)
{
	if (NULL != cache)
	{
		free(cache->DataCache);
		cache->DataCache = NULL;
		free(cache);
	}
}

DWORD listWavData(WAV_CACHE *cache)
{
	DWORD n = 0;
	DWORD n_samp = 0;
	PCM_8B1C_SAMP *p81 = (PCM_8B1C_SAMP *)cache->DataCache;
	PCM_8B2C_SAMP *p82 = (PCM_8B2C_SAMP *)cache->DataCache;
	PCM_16B1C_SAMP *p161 = (PCM_16B1C_SAMP *)cache->DataCache;
	PCM_16B2C_SAMP *p162 = (PCM_16B2C_SAMP *)cache->DataCache;
	short l, r;

	if (8 == cache->Format.wBitsPerSample)
	{
		if (1 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_8B1C_SAMP);
			for (n = 0; n < n_samp; n++)
			{
				printf("%ld\t%d\t%d\t\n", n, p81->CH0Data, p81->CH0Data);
				p81++;
			}
			p81 = NULL;
			return n;
		}

		if (2 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_8B2C_SAMP);
			for (n = 0; n < n_samp; n++)
			{
				printf("%ld\t%d\t%d\t\n", n, p82->CH0Data, p82->CH1Data);
				p82++;
			}
			p82 = NULL;
			return n;
		}
	}

	if (16 == cache->Format.wBitsPerSample)
	{
		if (1 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_16B1C_SAMP);
			for (n = 0; n < n_samp; n++)
			{
				l = r = p161->CH0Data;
				printf("%ld\t%d\t%d\t\n", n, l, r);
				p161++;
			}
			p161 = NULL;
			return n;
		}

		if (2 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_16B2C_SAMP);
			for (n = 0; n < n_samp; n++)
			{
				l = p162->CH0Data;
				r = p162->CH1Data;
				printf("%ld\t%d\t%d\t\n", n, l, r);
				p162++;
			}
			p162 = NULL;
			return n;
		}
	}

	err(DecodeErr);
	p81 = NULL;
	p82 = NULL;
	p161 = NULL;
	p162 = NULL;
	return n;
}

static BMP4bit_CACHE *cache4bitBmp(DWORD w, LONG h)//建立BMP缓存
{
	DWORD lineLen = (((w * 4) + 31) >> 5) << 2; //每行像素补齐后字节数
	BMP4bit_CACHE *out = NULL;
	//DWORD dataOffset = sizeof(BMP_INFOHEADER) + sizeof(BMP_RGBQUAD) * 16;
	size_t cacheSize = sizeof(BMP4bit_CACHE) + lineLen * h;
	if (0 == h || w <= 0)
		return NULL;
	out = (BMP4bit_CACHE *)calloc(cacheSize, sizeof(BYTE));
	out->info.biSize = sizeof(BMP_INFOHEADER);
	out->info.biWidth = w;
	out->info.biHeight = h;
	out->info.biPlanes = 1;
	out->info.biBitCount = 4;
	out->info.biComperession = 0;
	out->info.biSizeImage = lineLen * h;
	out->info.biXPelsPerMeter = 0;
	out->info.biYPelsPerMeter = 0;
	out->info.biClrUsed = 0;
	out->info.biClrImportant = 0;
	out->quad[0].rgbBlue = 255;
	out->quad[1].rgbGreen = 255;
	out->quad[2].rgbRed = 255;
	out->data = (BYTE *)out + sizeof(BMP4bit_CACHE);
	return out;
}

void delCache4bitBmp(BMP4bit_CACHE *cache)//清除BMP缓存
{
	if (NULL != cache)
	{
		cache->data = NULL;
		free(cache);
	}
}

static WORD makeRleline(short pcmdata, BYTE *line, DWORD w)//建立BMP行数据，RLE4压缩
{
	DWORD useByte = 0;
	int k = 0;
	int data = pcmdata;
	int l,z_blak,f_blak,z_data,f_data;

	if (512 >= w)//8bit
	{
		data = (unsigned char)pcmdata;
		data = w/2 - data;
	}
	if (data < 0)//16bit -
	{
		f_data = 0 - data;
		z_data = 1;
	}
	else//16bit +
	{
		f_data = 0;
		z_data = data + 1;
	}
	l = w/2;
	z_blak = l - z_data;
	f_blak = l - f_data;
	data = z_data + f_data;
	/*init*/

	for (k = f_blak / 255; k > 0; k--)
	{
		*line = 0xFF;
		line++;
		*line = 0x00;
		line++;
		useByte += 2;
	}
	if (0 < f_blak % 255)
	{
		*line = f_blak % 255;
		line++;
		*line = 0x00;
		line++;
		useByte += 2;
	}
	/*skip blak*/

	for (k = data / 255; k > 0; k--)
	{
		*line = 0xFF;
		line++;
		*line = 0x11;
		line++;
		useByte += 2;
	}
	if (0 < data % 255)
	{
		*line = data % 255;
		line++;
		*line = 0x11;
		line++;
		useByte += 2;
	}
	/*draw line*/

	for (k = z_blak / 255; k > 0; k--)
	{
		*line = 0xFF;
		line++;
		*line = 0x00;
		line++;
		useByte += 2;
	}
	if (0 < z_blak % 255)
	{
		*line = z_blak % 255;
		line++;
		*line = 0x00;
		line++;
		useByte += 2;
	}
	/*last blak*/

	*line = 0x00;
	line++;
	*line = 0x00;
	line++;
	useByte += 2;
	/*end line*/

	return useByte;
}

BMP4bit_CACHE *makeRleBmpData(WAV_CACHE *cache, LONG sample)//建立BMP数据，RLE4压缩
{
	DWORD n = 0;
	DWORD w = 0;
	DWORD n_samp = 0;
	DWORD lineLen = 0;
	DWORD dataSize = 0;
	short l, r;
	BYTE l8,r8;
	int RATIO = 1;
	PCM_8B1C_SAMP *p81 = (PCM_8B1C_SAMP *)cache->DataCache;
	PCM_8B2C_SAMP *p82 = (PCM_8B2C_SAMP *)cache->DataCache;
	PCM_16B1C_SAMP *p161 = (PCM_16B1C_SAMP *)cache->DataCache;
	PCM_16B2C_SAMP *p162 = (PCM_16B2C_SAMP *)cache->DataCache;
	BYTE *px = NULL;
	BMP4bit_CACHE *bmp = NULL;

	if (8 == cache->Format.wBitsPerSample)
	{
		w = 256 / RATIO;
		bmp = cache4bitBmp(w, sample);
		px = (BYTE *)bmp->data;
		if (1 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_8B1C_SAMP);
			if (sample < n_samp)
				n_samp = sample;
			for (n = 0; n < n_samp; n++)
			{
				l8 = r8 = p81->CH0Data;
				lineLen = makeRleline(l8 / RATIO, px, w);
				px += lineLen;
				dataSize += lineLen;
				p81++;
			}
			*px = 0x00;
			px++;
			*px = 0x01;
			dataSize += 2;
			bmp->info.biSizeImage = dataSize;
			bmp->info.biComperession = 2;
			return bmp;
		}

		if (2 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_8B2C_SAMP);
			if (sample < n_samp)
				n_samp = sample;
			for (n = 0; n < n_samp; n++)
			{
				l8 = p82->CH0Data;
				r8 = p82->CH1Data;
				lineLen = makeRleline(l8 / RATIO, px, w);
				px += lineLen;
				dataSize += lineLen;
				p82++;
			}
			*px = 0x00;
			px++;
			*px = 0x01;
			dataSize += 2;
			bmp->info.biSizeImage = dataSize;
			bmp->info.biComperession = 2;
			return bmp;
		}
	}

	if (16 == cache->Format.wBitsPerSample)
	{
		RATIO = 64 ;
		w = 65536 / RATIO;
		bmp = cache4bitBmp(w, sample);
		px = (BYTE *)bmp->data;
		if (1 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_16B1C_SAMP);
			if (sample < n_samp)
				n_samp = sample;
			for (n = 0; n < n_samp; n++)
			{
				l = r = (short)p161->CH0Data;
				lineLen = makeRleline(l / RATIO, px, w);
				px += lineLen;
				dataSize += lineLen;
				p161++;
			}
			*px = 0x00;
			px++;
			*px = 0x01;
			dataSize += 2;
			bmp->info.biSizeImage = dataSize;
			bmp->info.biComperession = 2;
			return bmp;
		}

		if (2 == cache->Format.wChannels)
		{
			n_samp = cache->DataSize / sizeof(PCM_16B2C_SAMP);
			if (sample < n_samp)
				n_samp = sample;
			for (n = 0; n < n_samp; n++)
			{
				r = (short)p162->CH1Data;
				l = (short)p162->CH0Data;
				lineLen = makeRleline(l / RATIO, px, w);
				px += lineLen;
				dataSize += lineLen;
				p162++;
			}
			*px = 0x00;
			px++;
			*px = 0x01;
			dataSize += 2;
			bmp->info.biSizeImage = dataSize;
			bmp->info.biComperession = 2;
			return bmp;
		}
	}

	err(DecodeErr);
	p81 = NULL;
	p82 = NULL;
	p161 = NULL;
	p162 = NULL;
	return NULL;
}

void wirteBmpFile(BMP4bit_CACHE *cache, FILE *file)//写入BMP文件
{
	if (NULL == file)
	{
		err(FileErr);
		return;
	}
	int fileHeadSize = sizeof(BMP_FILEHEADER);
	int bmpInfoSize = sizeof(BMP_INFOHEADER);
	int quadSize = sizeof(BMP_RGBQUAD) * 16;
	BMP_FILEHEADER *pFileHead = (BMP_FILEHEADER *)calloc(fileHeadSize, sizeof(BYTE));
	pFileHead->bfSize = 2 + fileHeadSize + bmpInfoSize + quadSize + cache->info.biSizeImage;
	pFileHead->bfReserved1 = 0;
	pFileHead->bfReserved2 = 0;
	pFileHead->bfOffset = 2 + fileHeadSize + bmpInfoSize + quadSize;
	fprintf(file, "%c%c", 'B', 'M');
	fwrite(pFileHead, fileHeadSize, 1, file);
	fwrite(cache, bmpInfoSize + quadSize, 1, file);
	fwrite(cache->data, cache->info.biSizeImage, 1, file);
	free(pFileHead);
	pFileHead = NULL;
	printf("Wirte BMP success\n");
}

#endif
